use std::{fmt::Debug, marker::PhantomData};

use super::*;

#[derive(Clone, Copy)]
pub struct LookupList<'a, T: LookupSubtable<'a>> {
	// pub lookup_count: u16,                         // Number of elements in the lookupOffsets array.
	pub lookup_offsets: LazyArray16<'a, Offset16>, // Array of offsets to Lookup tables, from beginning of LookupList.

	pub data: &'a [u8],
	_t: PhantomData<T>,
}
impl<'a, T: LookupSubtable<'a>> FromSlice<'a> for LookupList<'a, T> {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			lookup_offsets: s.parse_array16()?,
			data,
			_t: Default::default(),
		})
	}
}
impl<'a, T: LookupSubtable<'a>> LookupList<'a, T> {
	pub fn get_lookup(&self, index: u16) -> Option<Lookup<'a, T>> {
		self.lookup_offsets.get(index).and_then(|r| self.get_lookup_(&r))
	}
	pub fn get_lookup_(&self, r: &Offset16) -> Option<Lookup<'a, T>> {
		Lookup::parse(self.data.get(r.to_usize()..)?)
	}
	pub fn iter(&'a self) -> impl Iterator<Item = lookup::Lookup<'a, T>> {
		self.lookup_offsets.into_iter().filter_map(|r| self.get_lookup_(&r))
	}
}
impl<'a, T: LookupSubtable<'a> + Debug> core::fmt::Debug for LookupList<'a, T> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.debug_list()
			.entries(self.lookup_offsets.into_iter().map(|r| self.get_lookup_(&r)))
			.finish()
	}
}

pub trait LookupSubtable<'a>: Sized {
	/// Parses raw data.
	fn parse(data: &'a [u8], kind: u16) -> Option<Self>;
}

#[derive(Clone)]
pub struct Lookup<'a, T: LookupSubtable<'a>> {
	pub lookup_type: u16,        // Different enumerations for GSUB and GPOS.
	pub lookup_flag: LookupFlag, // Lookup qualifiers.
	// pub sub_table_count: u16,                        // Number of elements in the subtableOffsets array.
	pub subtable_offsets: LazyArray16<'a, Offset16>, // Array of offsets to lookup subtables, from beginning of Lookup table.
	pub mark_filtering_set: Option<u16>, // Index (base 0) into GDEF mark glyph sets structure. This field is only present if the USE_MARK_FILTERING_SET lookup flag is set.

	pub data: &'a [u8],
	pub _t: PhantomData<T>,
}
impl<'a, T: LookupSubtable<'a>> Lookup<'a, T> {
	pub fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		let lookup_type = s.read()?;
		let lookup_flag = s.read()?;
		Some(Self {
			lookup_type,
			lookup_flag,
			subtable_offsets: s.parse_array16()?,
			mark_filtering_set: if lookup_flag.contains(LookupFlag::USE_MARK_FILTERING_SET) {
				Some(s.read()?)
			} else {
				None
			},
			data,
			_t: Default::default(),
		})
	}
	pub fn get_subtable(&self, index: u16) -> Option<T> {
		self.subtable_offsets.get(index).and_then(|r| self.get_subtable_(r))
	}
	pub fn get_subtable_(&self, r: Offset16) -> Option<T> {
		T::parse(self.data.get(r.to_usize()..)?, self.lookup_type)
	}
	pub fn iter(&'a self) -> impl Iterator<Item = T> + use<'a, T> {
		self.subtable_offsets.into_iter().filter_map(|r| self.get_subtable_(r))
	}
}
impl<'a, T: LookupSubtable<'a> + Debug> core::fmt::Debug for Lookup<'a, T> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.debug_struct("Lookup")
			.field("lookup_type", &self.lookup_type)
			.field("lookup_flag", &self.lookup_flag)
			.field("subtable_offsets", &self.subtable_offsets.len())
			.field("mark_filtering_set", &self.mark_filtering_set)
			.field(
				"subtables",
				&self.subtable_offsets.into_iter().map(|r| self.get_subtable_(r)).collect::<Vec<_>>(),
			)
			// .field("data", &self.data)
			.finish()
	}
}

#[derive(Debug, Clone, Copy)]
pub struct LookupFlag(pub u16);
impl LookupFlag {
	pub const RIGHT_TO_LEFT: LookupFlag = LookupFlag(0x0001); // This bit is used only in relation to cursive attachment positioning (GPOS lookup type 3). When this bit is set, the last glyph in a matched input sequence will be positioned on the baseline.
	pub const IGNORE_BASE_GLYPHS: LookupFlag = LookupFlag(0x0002); // If set, skips over base glyphs
	pub const IGNORE_LIGATURES: LookupFlag = LookupFlag(0x0004); // If set, skips over ligatures
	pub const IGNORE_MARKS: LookupFlag = LookupFlag(0x0008); // If set, skips over all combining marks
	pub const USE_MARK_FILTERING_SET: LookupFlag = LookupFlag(0x0010); // If set, indicates that the Lookup table structure includes the markFilteringSet field. The layout engine skips over all mark glyphs not in the mark filtering set indicated.

	// 0x00E0	reserved	// For future use (Set to zero)

	pub const MARK_ATTACHMENT_CLASS_FILTER: LookupFlag = LookupFlag(0xFF00); // If not zero, skips over all marks not in the specified mark attachment class.

	#[inline]
	pub const fn contains(self, other: Self) -> bool {
		self.0 & other.0 == other.0
	}
}
impl FromData for LookupFlag {
	const SIZE: usize = 2;
	fn parse(data: &[u8]) -> Option<Self> {
		Some(Self(u16::parse(data)?))
	}
}
